﻿/* Created by Jordan Zhang 09/25/2010.

Copyright (c) 2010 Jordan Zhang

Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

 */

using System;
using System.Collections.Specialized;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.HtmlControls;
using System.Web.UI.WebControls;
using System.Xml;
using System.IO;
using System.Text;

namespace ServiceExplorer
{
    public enum DocType
    {
        WSDL = 0,
        XSD = 1
    }

    public class AttributedElementInfo
    {
        private string _elementName;
        private string _attributeName;
        private string _attributeValue;
        private string _attributeType;

        public AttributedElementInfo(string ename, string aName, string aValue, string aType)
        {
            _elementName = ename;
            _attributeName = aName;
            _attributeValue = aValue;
            _attributeType = aType;
            
        }

        public string ElementName
        {
            get { return _elementName; }
        }

        public string AttributeName
        {
            get { return _attributeName; }
        }

        public string AttributeValue
        {
            get { return _attributeValue; }
        }

        public string AttributeType
        {
            get { return _attributeType; }
        }

    }

    public class ComplexTypeInfo
    {
        private string _name;
        private string _type;
        private string _minOccurs;
        private string _maxOccurs;
        private string _note;

        public ComplexTypeInfo(string name, string type, string minOccurs, string maxOccurs, string note)
        {
            _name = name;
            _type = type;
            _minOccurs = minOccurs;
            _maxOccurs = maxOccurs;
            _note = note;
        }

        public string Name
        {
            get { return _name; }
        }

        public string Type
        {
            get { return _type; }
        }

        public string MinOccurs
        {
            get { return _minOccurs; }
        }

        public string MaxOccurs
        {
            get { return _maxOccurs; }
        }

        public string Note
        {
            get { return _note; }
        }
    }

    public partial class TypeViewer : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }
        
        protected void ViewComplexTypes()
        {
            XmlDocument doc = new XmlDocument();
            ProcessXSD(doc,AddToDropDownList);
            if (this.DropDownList_Object.Items.Count > 0)
            {
                this.DropDownList_Object.SelectedIndex = 0;
            }

            ProcessXSD(doc, BindGridViewToWSDLInfo);
            ProcessXSD(doc, BindToGridViewBySelectedDDLValue);
            
            if (CheckBox_TypeView.Checked) this.GridView_ObjectField.Visible = true;
            else this.GridView_ObjectField.Visible = false;

            if (CheckBox_WSDLDef.Checked) this.GridView_WSDLDef.Visible = true;
            else this.GridView_WSDLDef.Visible = false;
        }//protected void ViewComplexTypes()

        private void ProcessXSD(XmlDocument doc, Del del)
        {
            try
            {
                doc.LoadXml(this.TextBox_XSD.Text);

                //namespace detection on common before delegate.
                XmlNodeList wsdlNodes = doc.GetElementsByTagName("wsdl:definitions");//not xpath/wsdl:types");
                XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
                if (wsdlNodes != null)
                {
                    if (wsdlNodes.Count > 0)
                    {
                        this.RadioButtonList_DocType.SelectedIndex = (int)DocType.WSDL;
                        this.CheckBox_WSDLDef.Visible = true;
                        nsmgr.AddNamespace("wsdl", "http://schemas.xmlsoap.org/wsdl/");
                        nsmgr.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema");
                    }
                    else//if (wsdlNodes.Count > 0)
                    {
                        XmlNodeList xsdNodes = doc.GetElementsByTagName("xs:schema");
                        if (xsdNodes.Count > 0)
                        {
                            this.RadioButtonList_DocType.SelectedIndex = (int)DocType.XSD;
                            this.CheckBox_WSDLDef.Visible = false;
                            nsmgr.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema");
                        }
                        else
                        {
                            ShowErrorMessage("Document you pasted is neither a XSD nor a WSDL.");
                        }
                    }
                }

                List<string> nameList = del(doc, nsmgr);

                if (nameList.Count == 0)
                {
                    //this.ShowErrorMessage("Are you sure you pasted a well-formed XSD?");
                }
            }
            catch (Exception ex)
            {
                this.ShowErrorMessage("It does seems to be well-formed XML. Exception in Loading: " + ex.Message);
            }
        }

        public delegate List<string> Del(XmlDocument doc, XmlNamespaceManager nsmgr);

        private List<string> AddToDropDownList(XmlDocument doc, XmlNamespaceManager nsmgr)
        {
            List<string> nameList = new List<string>();
            XmlNodeList complexTypeNodes = null;
            string xPath = "xs:schema/xs:complexType[@name]";
            if (this.RadioButtonList_DocType.SelectedIndex == (int)DocType.WSDL)
            {
                xPath = "wsdl:definitions/wsdl:types/"+ xPath;             
            }
            
            if (this.RadioButtonList_DocType.SelectedIndex != -1)
            {
                complexTypeNodes = doc.SelectNodes(xPath, nsmgr);
            }

            AddNodeToList(complexTypeNodes, nameList);
            nameList.Sort();
            foreach (string name in nameList)
            {
                this.DropDownList_Object.Items.Add(new ListItem(name));
            }
            return nameList;
        }            

        private void AddNodeToList(XmlNodeList nodes, List<string> nameList)
        {
            foreach (XmlNode node in nodes)
            {
                if (node.Name == "xs:complexType" && node.Attributes != null)
                {
                    if (node.Attributes["name"] != null)
                    {
                        nameList.Add(node.Attributes["name"].InnerText);
                        //this.DropDownList_Object.Items.Add(new ListItem(node.Attributes["name"].InnerText));
                    }
                }
               
                if (node.ChildNodes != null)
                {
                    if (node.ChildNodes.Count > 0)
                    {
                        AddNodeToList(node.ChildNodes, nameList);
                    }//if (node.ChildNodes.Count > 0)
                }//if (node.ChildNodes != null)
            }//foreach (XmlNode node in nodes)
        }

        //this is the diff method to write.
        private List<string> BindToGridViewBySelectedDDLValue(XmlDocument doc, XmlNamespaceManager nsmgr)
        {
            List<string> nameList = new List<string>();
            try
            {
                //XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
                //nsmgr.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema");
                string xPathPrefix = "xs:schema/xs:complexType[@name='" + this.DropDownList_Object.SelectedValue + "']";// +"/xs:sequence/";
                if (this.RadioButtonList_DocType.SelectedIndex == (int)DocType.WSDL)
                {
                    xPathPrefix = "wsdl:definitions/wsdl:types/" + xPathPrefix;
                }

                string xPath = xPathPrefix+"/xs:sequence/*"; 

                XmlNodeList nodes = doc.SelectNodes(xPath, nsmgr);
                
                if (nodes != null)
                {
                    if (nodes.Count == 0)
                    {//check if Base Type                       
                        xPath = xPathPrefix + "/xs:complexContent/xs:extension/xs:sequence/*";
                        string xPathBase = xPathPrefix + "/xs:complexContent/xs:extension[@base]";
                        XmlNode baseNode = doc.SelectSingleNode(xPathBase, nsmgr);
                        this.Label_BaseInfo.Visible = true;
                        this.LinkButton_BaseType.Visible = true;
                        this.Label_BaseInfo.Text = "Base Type is ";
                        this.LinkButton_BaseType.Text = (baseNode.Attributes["base"] == null ? "" : baseNode.Attributes["base"].InnerText);
                        nodes = doc.SelectNodes(xPath, nsmgr);
                    }//if (nodes.Count == 0)                    
                    else
                    {
                        this.Label_BaseInfo.Visible = false;
                        this.LinkButton_BaseType.Visible = false;
                    }
                    List<ComplexTypeInfo> list = new List<ComplexTypeInfo>();
               

                    if (nodes.Count > 0)
                    {
                        //XmlNodeList typeNodes = nodes[0].SelectNodes("xs:complexType/xs:sequence/*", nsmgr);
                        //foreach (XmlNode node in nodes[0].ChildNodes[0].ChildNodes)
                        foreach (XmlNode node in nodes)
                        {
                            //get documentation
                            //xs:annotation><xs:documentation
                            XmlNode docNode = node.SelectSingleNode("xs:annotation/xs:documentation", nsmgr);
                            list.Add(new ComplexTypeInfo(node.Attributes["name"] == null ? "Undefined" : node.Attributes["name"].InnerText
                                 , node.Attributes["type"] == null ? "Undefined" : node.Attributes["type"].InnerText
                                 ,node.Attributes["minOccurs"] == null ? "Undefined" : node.Attributes["minOccurs"].InnerText
                                 ,node.Attributes["maxOccurs"] == null ? "Undefined" : node.Attributes["maxOccurs"].InnerText
                                 , docNode == null ? "N/A" : docNode.InnerText));
                                nameList.Add(node.Attributes["name"].InnerText);
                        }//foreach(XmlNode node in nodes)
                        this.GridView_ObjectField.Caption = this.DropDownList_Object.SelectedValue + " Type Information";
                        this.GridView_ObjectField.DataSource = list;
                        this.GridView_ObjectField.DataBind();
                        ShowInfoMessage(this.DropDownList_Object.SelectedValue+" Type information from "+this.Label_FileName.Text+" is down below");
                    }
                }
            }
            catch (Exception ex)
            {
                this.ShowErrorMessage("Exception in Loading: " + ex.Message);
            }
            return nameList;//for delegate purpose only.
        }

        private List<string> BindGridViewToWSDLInfo(XmlDocument doc, XmlNamespaceManager nsmgr)
        {
            List<string> nameList = new List<string>();
            try
            {
                //XmlNamespaceManager nsmgr = new XmlNamespaceManager(doc.NameTable);
                //nsmgr.AddNamespace("xs", "http://www.w3.org/2001/XMLSchema");
                string xPath = null;// "xs:schema/xs:complexType[@name='" + this.DropDownList_Object.SelectedValue + "']";// +"/xs:sequence/";
                if (this.RadioButtonList_DocType.SelectedIndex == (int)DocType.WSDL)
                {
                    xPath = "wsdl:definitions/*[@name]";//child node with element name.
                }
                else return nameList;
                
                List<AttributedElementInfo> list = new List<AttributedElementInfo>();
                XmlNode wsdlDefNode = doc.SelectSingleNode("wsdl:definitions", nsmgr);
                foreach (XmlAttribute attribute in wsdlDefNode.Attributes)
                {
                    list.Add(new AttributedElementInfo("wsdl:definitions", attribute.Name, attribute.Value
                               , "N/A"));
                }

                XmlNodeList nodes = doc.SelectNodes(xPath, nsmgr);
                if (nodes != null)
                { 
                    foreach (XmlNode node in nodes)
                    {
                            //get documentation
                            //xs:annotation><xs:documentation
                            //XmlNode docNode = node.SelectSingleNode("xs:annotation/xs:documentation", nsmgr);
                            list.Add(new AttributedElementInfo("wsdl:definitions/"+node.Name, "name", node.Attributes["name"] == null ? "N/A" : node.Attributes["name"].Value                                 
                                , node.Attributes["type"] == null ? "N/A" : node.Attributes["type"].InnerText
                                 ));
                            nameList.Add(node.Attributes["name"].InnerText);
                    }//foreach(XmlNode node in nodes)                                         
                }
                xPath = "wsdl:definitions/wsdl:binding/*";
                nodes = doc.SelectNodes(xPath, nsmgr);
                if (nodes != null)
                {
                    foreach (XmlNode node in nodes)
                    {
                        //get documentation
                        //xs:annotation><xs:documentation
                        //XmlNode docNode = node.SelectSingleNode("xs:annotation/xs:documentation", nsmgr);
                        list.Add(new AttributedElementInfo("wsdl:definitions/wsdl:binding/" + node.Name, node.Attributes[0] == null ? "N/A" : node.Attributes[0].Name, node.Attributes[0] == null ? "N/A" : node.Attributes[0].Value
                            , node.Attributes["type"] == null ? "N/A" : node.Attributes["type"].InnerText
                             ));
                        //nameList.Add(node.Attributes["name"].InnerText);
                    }//foreach(XmlNode node in nodes)                                         
                }

                xPath = "wsdl:definitions/wsdl:binding/wsdl:operation/*";
                nodes = doc.SelectNodes(xPath, nsmgr);
                if (nodes != null)
                {
                    foreach (XmlNode node in nodes)
                    {
                        //get documentation
                        //xs:annotation><xs:documentation
                        //XmlNode docNode = node.SelectSingleNode("xs:annotation/xs:documentation", nsmgr);
                        list.Add(new AttributedElementInfo("wsdl:definitions/wsdl:binding/wsdl:operation/" + node.Name, node.Attributes[0] == null ? "N/A" : node.Attributes[0].Name, node.Attributes[0] == null ? "N/A" : node.Attributes[0].Value
                            , node.Attributes["type"] == null ? "N/A" : node.Attributes["type"].InnerText
                             ));
                        //nameList.Add(node.Attributes["name"].InnerText);
                    }//foreach(XmlNode node in nodes)                                         
                }

                this.GridView_WSDLDef.DataSource = list;
                this.GridView_WSDLDef.DataBind();

            }
            catch (Exception ex)
            {
                this.ShowErrorMessage("Exception in Loading: " + ex.Message);
            }
            return nameList;//for delegate purpose only.
        }   
        
        protected void DropDownList_Object_SelectedIndexChanged(object sender, EventArgs e)
        {
           this.Label_Info.Text = null;
           XmlDocument doc = new XmlDocument();
           ProcessXSD(doc, BindToGridViewBySelectedDDLValue);
        }//private void AddNodeToList(XmlNodeList nodes)

        private void ShowErrorMessage(string msg)
        {
            this.Label_Info.Text = "Error:" + msg;
            this.Label_Info.ForeColor = System.Drawing.Color.Red;
        }

        private void ShowInfoMessage(string msg)
        {
            this.Label_Info.Text = msg;
            this.Label_Info.ForeColor = System.Drawing.Color.Blue;
        }

        
        protected void Button_Upload_Click(object sender, EventArgs e)
        {      
            if (FileUpload1.HasFile)
            {
                this.DropDownList_Object.Items.Clear();
                this.ShowInfoMessage(null);
                this.ShowErrorMessage(null);
                this.GridView_WSDLDef.DataSource = null;
                this.GridView_WSDLDef.DataBind();

                string t = Server.MapPath("~/Uploads/");
                string who = Request.ServerVariables["remote_user"].Replace(@"\", "_");
                //who = who.Replace('.', '_');
                this.Label_FileName.Text = FileUpload1.FileName;
                string postFix = null;
                if (FileUpload1.FileName.ToLower().EndsWith("wsdl")) postFix = ".wsdl";
                else if (FileUpload1.FileName.ToLower().EndsWith("xsd")) postFix = ".xsd";

                string xmlFileName = FileUpload1.FileName + "_" + who + "_" + DateTime.Now.ToString("yyyyMMddHHmmss") + postFix;//DateTime.Now.ToString("yyyyMMddHHmmss")+".wsdl";
                string filePath = "~/Uploads/" + xmlFileName;// "~/Uploads/" +FileUpload1.FileName+"_"+who + "_" + DateTime.Now.ToString("yyyyMMddHHmmss");
                string serverMapPath = Server.MapPath(filePath);
                FileUpload1.SaveAs(serverMapPath);

                // Notify the user that the file was uploaded successfully.
                ShowInfoMessage("Your file was uploaded successfully.");
                // Call a helper routine to display the contents
                // of the file to upload.
                DisplayFileContents(FileUpload1.PostedFile, xmlFileName);
                ViewComplexTypes();
               
            }
            else
            {
                // Notify the user that a file was not uploaded.
                ShowErrorMessage("You did not specify a WSDL or XSD file to upload & view.");
            }
        }
        
        private void DisplayFileContents(HttpPostedFile file, string fileNamePosted)
        {  
            
            // Get the length of the file.
            int fileLen = FileUpload1.PostedFile.ContentLength;
            // Create a byte array to hold the contents of the file.
            Byte[] input = new Byte[fileLen];
            file.InputStream.Read(input, 0, Convert.ToInt32(file.InputStream.Length));
            this.TextBox_XSD.Text = System.Text.ASCIIEncoding.ASCII.GetString(input);
            
            HtmlControl iFrame = (HtmlControl)this.FindControl("iFrame");
            iFrame.Attributes["src"] = "Uploads/"+fileNamePosted;//20100923133526.xml";// +file.FileName;//fullPath; //sFile;

            if (CheckBox_TreeView.Checked) this.iFrame.Visible = true;
            else this.iFrame.Visible = false;

            if (CheckBox_TextView.Checked) this.TextBox_XSD.Visible = true;
            else this.TextBox_XSD.Visible = false;

        }

        protected void LinkButtonBaseType_Click(object sender, EventArgs e)
        {
            this.BindDDLGridViewByTypeName(this.LinkButton_BaseType.Text);
        }

        private void BindDDLGridViewByTypeName(string typeName)
        {
            this.DropDownList_Object.SelectedIndex = -1;
            foreach (ListItem item in this.DropDownList_Object.Items)
            {
                if (item.Value == typeName)
                {
                    item.Selected = true;
                    break;
                }
            }
            XmlDocument doc = new XmlDocument();
            ProcessXSD(doc, BindToGridViewBySelectedDDLValue);
        }

        protected void LinkButton_Type_Click(object sender, EventArgs e)
        {
            LinkButton lkButton = (LinkButton)sender;
            if (lkButton.Text.StartsWith("xs:"))
            {
                return;
            }
            this.BindDDLGridViewByTypeName(lkButton.Text);
        }

        protected void GridView_ObjectField_RowDataBound(object sender, GridViewRowEventArgs e)
        {
            GridViewRow row = e.Row;
            if (row.RowType != DataControlRowType.DataRow) return;
            LinkButton btnType = (LinkButton)row.FindControl("LinkButton_Type");
            if (btnType.Text.StartsWith("xs:"))
            {
                btnType.Enabled = false;
                btnType.ForeColor = System.Drawing.Color.Black;
            }
        }

        protected void GridView_ObjectField_SelectedIndexChanged(object sender, EventArgs e)
        {

        }

        protected void CheckBox_TreeView_CheckedChanged(object sender, EventArgs e)
        {
            if(CheckBox_TreeView.Checked) this.iFrame.Visible = true;
            else this.iFrame.Visible = false;
        }

        protected void CheckBox_TextView_CheckedChanged(object sender, EventArgs e)
        {
            if (CheckBox_TextView.Checked) this.TextBox_XSD.Visible = true;
            else this.TextBox_XSD.Visible = false;
        }

        protected void CheckBox_TypeView_CheckedChanged(object sender, EventArgs e)
        {
            if (CheckBox_TypeView.Checked) this.GridView_ObjectField.Visible = true;
            else this.GridView_ObjectField.Visible = false;
        }

        protected void CheckBox_WSDLDef_CheckedChanged(object sender, EventArgs e)
        {
            if (CheckBox_WSDLDef.Checked) this.GridView_WSDLDef.Visible = true;
            else this.GridView_WSDLDef.Visible = false;
        }// protected void LinkButtonBaseType_Click(object sender, EventArgs e)      
            
    }
}
